﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;

namespace MASAS.MSM.DomainLayer.Logging
{
    
    public sealed class Logger
    {

        // Private Static members...
        private static volatile Logger _instance;
        private static          object _syncRoot = new Object();
        
        // Private members...
        private const string    _logSubFolder   = "\\MASAS\\MasasService\\";
        private const string    _logFileName    = "masasServiceLog_";
        private const string    _logFileExt     = ".log";
        private AsyncOperation  _asyncOperation = null;
        private string          _logFile        = string.Empty;
        private LogEventType    _logEventTypes  = LogEventType.Error;   // Log only Errors by default.
        private bool            _logEvents      = true;                 // Log event to file.

        private const string    _eventLogSource = "MASAS Service Module";
        private const string    _eventLog       = "Application";

        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static Logger Instance
        {
            get
            {
                if ( _instance == null )
                {
                    lock ( _syncRoot )
                    {
                        if ( _instance == null )
                        {
                            _instance = new Logger();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// Gets or sets the log event types.
        /// </summary>
        /// <value>
        /// The log event types.
        /// </value>
        public LogEventType LogEventTypes {
            get { return _logEventTypes; }
            set { _logEventTypes = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [log events].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [log events]; otherwise, <c>false</c>.
        /// </value>
        public bool LogEvents {
            get { return _logEvents; }
            set { _logEvents = value; }
        }

        public event EventHandler<LogEventArgs> NewLogEntry;

        /// <summary>
        /// Adds a new log entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        public static void AddLogEntry( string entry )
        {
            Instance.OnNewLogEntry( new LogEventArgs( entry ) );
        }

        /// <summary>
        /// Adds the log entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="eventType">The type of event.</param>
        public static void AddLogEntry( string entry, LogEventType eventType )
        {
            Instance.OnNewLogEntry( new LogEventArgs( entry, eventType ) );
        }

        /// <summary>
        /// Adds a new log entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="exception">The exception.</param>
        public static void AddLogEntry( string entry, Exception exception )
        {
            Instance.OnNewLogEntry( new LogEventArgs( entry, exception ) );
        }

        /// <summary>
        /// Prevents a default instance of the <see cref="Logger"/> class from being created.
        /// </summary>
        private Logger()
        {
            // Create the async operation...
            _asyncOperation = AsyncOperationManager.CreateOperation( null );

            // Create our log folder if it doesn't exist...
            string logPath = Environment.GetFolderPath( Environment.SpecialFolder.CommonApplicationData ) + _logSubFolder;
            _logFile = logPath + _logFileName;

            // Make sure our logging folder exists...
            if( !Directory.Exists( logPath ) )
            {
                Directory.CreateDirectory( logPath );
            }

            // Let make sure our Event source is created...
            if( !EventLog.SourceExists( _eventLogSource ) )
            {
                EventLog.CreateEventSource( _eventLogSource, _eventLog );
            }
        }

        /// <summary>
        /// Raises the <see cref="E:NewLogEntry"/> event.
        /// </summary>
        /// <param name="logEventArgs">The <see cref="MASAS.MSM.DomainLayer.Logging.LogEventArgs"/> instance containing the event data.</param>
        private void OnNewLogEntry( LogEventArgs logEventArgs )
        {
            // Write the entry to the log file...
            WriteToLogFile( logEventArgs );

            // Trigger the event for all who are listening...
            _asyncOperation.Post( new SendOrPostCallback( delegate( object state )
            {
                // Make a temp copy of the event to avoid race conditions...
                EventHandler<LogEventArgs> handler = NewLogEntry;

                if( handler != null )
                {
                    handler( this, logEventArgs );
                }
            } ), null );

        }

        /// <summary>
        /// Writes to log file.
        /// </summary>
        /// <param name="logEventArgs">The <see cref="MASAS.MSM.DomainLayer.Logging.LogEventArgs"/> instance containing the event data.</param>
        private void WriteToLogFile( LogEventArgs logEventArgs )
        {
            // Generate the log file name...
            string logFile = _logFile + DateTime.Today.ToString( "ddMMyyyy" ) + _logFileExt;

            // If we have an error, log this to the Windows Logs...
            if( logEventArgs.EventType == LogEventType.Error )
            {
                EventLog.WriteEntry( _eventLogSource, logEventArgs.LogEntry, EventLogEntryType.Error );
            }

            // If an exception is raised, override the admin's choice and dump to the file...
            if( ( logEventArgs.Exception != null ) || ( _logEvents && logEventArgs.EventType >= _logEventTypes ) )
            {
                // Write the log event to the log file...
                File.AppendAllText( logFile, logEventArgs.ToString() + Environment.NewLine );

                // If an exception was raised, dump the contents to the file...
                if( logEventArgs.Exception != null )
                {
                    File.AppendAllText( logFile, "Exception Content - Start" + Environment.NewLine );
                    File.AppendAllText( logFile, logEventArgs.Exception.Message + Environment.NewLine );
                    File.AppendAllText( logFile, logEventArgs.Exception.StackTrace + Environment.NewLine );

                    if( logEventArgs.Exception.InnerException != null )
                    {
                        File.AppendAllText( logFile, "Inner Exception Content - Start" + Environment.NewLine );
                        File.AppendAllText( logFile, logEventArgs.Exception.InnerException.Message + Environment.NewLine );
                        File.AppendAllText( logFile, logEventArgs.Exception.InnerException.StackTrace + Environment.NewLine );
                        File.AppendAllText( logFile, "Inner Exception Content - End" + Environment.NewLine );
                    }

                    File.AppendAllText( logFile, "Exception Content - End" + Environment.NewLine );
                }
            }
        }

    }

}
